/* ME2MailMessage.cpp
 *
 * $Id: ME2MailMessage.cpp,v 1.1 2008/10/17 06:08:18 leon Exp $
 */

#include <time.h>
#include <iconv.h>

#include "ME2MailMessage.h"
#include "ME2MailEsc.h"

#ifdef _LIBICONV_H
#define LIBICONV_PLUG
#define icv_open(a, b) libiconv_open(a, b)
#define icv_close(a) libiconv_close(a)
#define icv(a, b, c, d, e) libiconv(a, b, c, d, e)
#else
#define icv_open(a, b) iconv_open(a, b)
#define icv_close(a) iconv_close(a)
#define icv(a, b, c, d, e) iconv(a, (char **) b, c, d, e)
#endif

CLStr ME2Iconv(LPCSTR key, LPCSTR src_charset, LPCSTR dst_charset) {
	if(dst_charset==NULL || src_charset==NULL || !strcasecmp(src_charset, dst_charset)) return key;
	if(!*src_charset || !strcasecmp(src_charset,"iso-8859-1") || !strcasecmp(src_charset, "auto")||!strcasecmp(src_charset,"gb2312")) src_charset="gbk";
	CLStr buf,val;
	iconv_t it = icv_open(dst_charset, src_charset);
	if(it != (iconv_t)-1) {
		unsigned int inlen = strlen(key);
		const char* instr = key;
		unsigned int out_left,out_size = inlen*4+16;
		out_left = out_size;
		char* out_p = val.getbuf(out_left);
		char* outstr = out_p;
		int ret = icv(it, &instr, &inlen, &out_p, &out_left);
		icv_close(it);	
		if(ret>=0) {
			buf.assign(outstr, out_size-out_left);
			return buf;
		}
	}
	return key;
}
	
CLStr ME2MailHeaders::GetFileName(LPCSTR dst_charset) const {
	// first get content-diposition's filename
	CLStr fileName = GetHeader("Content-Disposition", "filename", dst_charset);
	
	// if not found, get the content-type's name
	if(fileName.empty()) fileName = GetHeader("Content-Type","name", dst_charset);

	if(fileName.empty()) {
		if(GetHeader("Content-Type").findi("text/plain")!=-1) fileName.sprintf("ATT%08X.txt", this);
		else if(GetHeader("Content-Type").findi("text/html")!=-1) fileName.sprintf("ATT%08X.htm", this);
		else fileName.sprintf("ATT%08X.dat", this);
	}

	return fileName;
}
void ME2MailHeaders::MoveHeader(LPCSTR headerName, ME2MailHeaders* head) {
	CLStr val;
	GetValI(headerName, val);
	head->SetValI(headerName, val);
}
CLStr ME2MailHeaders::GetHeader(LPCSTR headerName, LPCSTR param/*=NULL*/, LPCSTR dst_charset/*=NULL*/) const
{
	CLStr val, key;
	if(!GetValI(headerName, val)) return "";
	if(param) {
		key = param;
		if(!key.cmpi(headerName)) {
			int begin = val.find(";");
			if(begin != -1) val = val.substr(0, begin);
		} else {
			int begin = val.findi(key + "=");
			if(begin == -1) return "";
			begin += key.length() + 1;
			int end = val.find(";", begin);
			if(end != -1) val = val.substr(begin, end - begin);
			else val = val.substr(begin);
			val.trim("\"");
		}
	}

	CLStr src_charset = "gb2312";
	RFC822_decode((LPBYTE)(LPCSTR)val, key.getbuf(val.length()*2), &src_charset);
	key.endlpstr();
	HZ_decode((LPSTR)(LPCSTR)key);
	key.endlpstr();

	//I hate Hichina, they use EUC_CN as EUC-CN
	src_charset.replacei("EUC_", "EUC-");
	
	if(dst_charset && *dst_charset && src_charset.length()>2 && !strcasecmp(dst_charset,"utf-8") && src_charset.cmpi(dst_charset)) {
		return ME2Iconv(key, src_charset, dst_charset);
	}
	
	return key;
}
int ME2MailHeaders::ReadHeaders(const ME2Str& mailhead) {
	int prev = 0;
	int next = 0;
	int colon;
	ME2Str line;
	CLStr lastline, key, val;

	while((next = mailhead.find("\n", prev)) != -1) {
		line = mailhead.substr(prev, next - prev);
		line.trim("\r");
		if(line.find_first_of(" \t") == 0) {
			lastline += CRLF;
			lastline += CLStr(line.m_data, line.length());
		} else {
			if(!lastline.empty() && (colon = lastline.find(":")) != -1) {
				key = lastline.substr(0, colon);
				val = lastline.substr(colon + 1);
				val.trim(" \t");
				SetValI(key, val);
			}		
			lastline.assign(line.m_data, line.length());
		}
		if(prev && line.empty()) { //double line find
			break;
		}
		prev = next + 1;
	}
	//Dump();
	return next;
}
int ME2MailHeaders::ReadHeaders(const CLStr& mailhead) {
	int prev = 0;
	int next = 0;
	int colon;
	CLStr line;
	CLStr lastline, key, val;

	while((next = mailhead.find("\n", prev)) != -1) {
		line = mailhead.substr(prev, next - prev);
		line.trim("\r");
		if(line.find_first_of(" \t") == 0) {
			lastline += CRLF;
			lastline += line;
		} else {
			if(!lastline.empty() && (colon = lastline.find(":")) != -1) {
				key = lastline.substr(0, colon);
				val = lastline.substr(colon + 1);
				val.trim(" \t");
				SetValI(key, val);
			}		
			lastline = line;
		}
		if(prev && line.empty()) { //double line find
			break;
		}
		prev = next + 1;
	}
	//Dump();
	return next;
}
ME2MailBodyPart::ME2MailBodyPart(ME2MailHeaders* header, CLStr* pBody, ME2MailBodyPart* par) {
	pParent = par;
	pHeaders = header;
	if(pBody) {
		pBody->trim("\r\n");
		CLStr encoding = header->GetHeader("Content-Transfer-Encoding");
		int	nSize = pBody->length() + 1024;
		if(encoding.findi("quoted") != -1 && encoding.findi("printable") != -1 )
			QuotedPrint_decode((LPCBYTE)(LPCSTR)*pBody, pBody->length(), (LPBYTE)content.getbuf(nSize), &nSize);
		else if(encoding.findi("base64") != -1)
			Base64_decode((LPCBYTE)(LPCSTR)*pBody, pBody->length(), (LPBYTE)content.getbuf(nSize), &nSize);
		else if( encoding.findi("uuencode") != -1 || encoding.findi("x-uue") != -1 || encoding.findi("x-uuencode") != -1)
			UU_decode((LPCBYTE)(LPCSTR)*pBody, pBody->length(), (LPBYTE)content.getbuf(nSize), &nSize);
		else {
			nSize = pBody->length();
			content = *pBody;
		}
		content.setmaxlength(nSize);
		if(!header->GetHeader("Content-type","charset").cmpi("hz-gb-2312")) {
			HZ_decode(content.getbuf(nSize));
			content.endlpstr();
		}
	}
}
ME2MailBodyPart::ME2MailBodyPart(ME2MailHeaders* header, ME2Str* pBody, ME2MailBodyPart* par) {
	pParent = par;
	pHeaders = header;
	if(pBody) {
		pBody->trim("\r\n");
		CLStr encoding = header->GetHeader("Content-Transfer-Encoding");
		int	nSize = pBody->length() + 1024;
		if(encoding.findi("quoted") != -1 && encoding.findi("printable") != -1 )
			QuotedPrint_decode((LPCBYTE)(pBody->m_data), pBody->length(), (LPBYTE)content.getbuf(nSize), &nSize);
		else if(encoding.findi("base64") != -1)
			Base64_decode((LPCBYTE)(pBody->m_data), pBody->length(), (LPBYTE)content.getbuf(nSize), &nSize);
		else if( encoding.findi("uuencode") != -1 || encoding.findi("x-uue") != -1 || encoding.findi("x-uuencode") != -1)
			UU_decode((LPCBYTE)(pBody->m_data), pBody->length(), (LPBYTE)content.getbuf(nSize), &nSize);
		else {
			nSize = pBody->length();
			content.assign(pBody->m_data, nSize);
		}
		content.setmaxlength(nSize);
		if(!header->GetHeader("Content-type","charset").cmpi("hz-gb-2312")) {
			HZ_decode(content.getbuf(nSize));
			content.endlpstr();
		}
	}
}


#ifdef _DEBUG
void ME2MailBodyPart::Dump() {
	printf("\n-----%x))))))))))\n", this);
	//if(pParent) pParent->Dump();
	pHeaders->Dump();
	printf("\nConent Begin-----\n%s\nConent End----\n", (LPCSTR)content);
	printf("\n-----%x((((((((((\n", this);
}
#endif

//////////////////////////////////////////////////////////////////////////////
// ME2MailMessage
//
//
//
//////////////////////////////////////////////////////////////////////////////
ME2MailMessage::ME2MailMessage(int bMIME) {
	Create(bMIME);
}
ME2MailMessage::ME2MailMessage(const CLStr& content) {
	ReadBodyPart(content, NULL);
}
ME2MailMessage::~ME2MailMessage() {
	Close();
}
ME2MailBodyPart* ME2MailMessage::GetBodyPart(LPCSTR cid) const {
	CLStr pcid;
	for(int i = 0; i < bodyParts.GetCount(); i++) {
		pcid = ((ME2MailBodyPart*)bodyParts[i])->pHeaders->GetHeader("Content-ID");
		pcid.trim("<>");
		if(pcid == cid) return (ME2MailBodyPart*)bodyParts[i];
	}
	return NULL;	
}
void ME2MailMessage::ReadBodyPart(const ME2Str& content, ME2MailBodyPart* pParent, int bDecodeAll/*=1*/)
{
	//first read head
	ME2MailHeaders* head = new ME2MailHeaders;
	if(!pParent) pHeaders = head;
	int next = head->ReadHeaders(content);
	ME2Str nextcontent = content.substr(next); 
	ME2Str tmp;

	if(head->GetHeader("Content-Type").findi("multipart/") != -1) {
		CLStr boundary = "--" + head->GetHeader("Content-Type","boundary");
		int prev = nextcontent.find(boundary);
		if(prev == -1) {	//find an error!
			if(!pParent)	bodyParts.AddTail(new ME2MailBodyPart(head, &nextcontent, pParent));
			else delete head;
		} else {
			tmp = nextcontent.substr(0, prev);
			ME2MailBodyPart* pMulti = new ME2MailBodyPart(head, &(tmp), pParent);
			multiParts.AddTail(pMulti);
			prev += boundary.length();
			while((next = nextcontent.find(boundary, prev)) != -1) {
				ReadBodyPart(nextcontent.substr(prev, next - prev), pMulti, bDecodeAll);
				prev = next + boundary.length();
				if(nextcontent.substr(prev, 2) == "--") break;
			}
			if(nextcontent.substr(prev, 2) != "--" && prev < nextcontent.length() - 1) {
				ReadBodyPart(nextcontent.substr(prev), pMulti, bDecodeAll); 
			}
		}
	} else if(bDecodeAll && head->GetHeader("Content-Type").findi("message/rfc822") != -1) {
		ME2MailBodyPart* pMulti = new ME2MailBodyPart(head, pParent);
		multiParts.AddTail(pMulti); 
		ReadBodyPart(nextcontent, pMulti, bDecodeAll);
	} else {
		bodyParts.AddTail(new ME2MailBodyPart(head, &nextcontent, pParent));
	}
}
void ME2MailMessage::ReadBodyPart(const CLStr& content, ME2MailBodyPart* pParent, int bDecodeAll/*=1*/) 
{
	//first read head

	ME2MailHeaders* head = new ME2MailHeaders;
	if(!pParent) pHeaders = head;
	int next = head->ReadHeaders(content);
	CLStr nextcontent = content.substr(next); 
	CLStr tmp;

	if(head->GetHeader("Content-Type").findi("multipart/") != -1) {
		CLStr boundary = "--" + head->GetHeader("Content-Type","boundary");
		int prev = nextcontent.find(boundary);
		if(prev == -1) {	//find an error!
			if(!pParent)	bodyParts.AddTail(new ME2MailBodyPart(head, &nextcontent, pParent));
			else delete head;
		} else {
			tmp = nextcontent.substr(0, prev);
			ME2MailBodyPart* pMulti = new ME2MailBodyPart(head, &(tmp), pParent);
			multiParts.AddTail(pMulti);
			prev += boundary.length();
			while((next = nextcontent.find(boundary, prev)) != -1) {
				ReadBodyPart(nextcontent.substr(prev, next - prev), pMulti, bDecodeAll);
				prev = next + boundary.length();
				if(nextcontent.substr(prev, 2) == "--") break;
			}
			if(nextcontent.substr(prev, 2) != "--" && prev < nextcontent.length() - 1) 
				ReadBodyPart(nextcontent.substr(prev), pMulti, bDecodeAll); 
		}
	} else if(bDecodeAll && head->GetHeader("Content-Type").findi("message/rfc822") != -1) {
		ME2MailBodyPart* pMulti = new ME2MailBodyPart(head, pParent);
		multiParts.AddTail(pMulti); 
		ReadBodyPart(nextcontent, pMulti, bDecodeAll);
	} else {
		bodyParts.AddTail(new ME2MailBodyPart(head, &nextcontent, pParent));
	}
}

void ME2MailHeaders::WriteHeaders(CLStr &head) const
{
	for(int i = 0; i < GetCount(); i++) {
		head += Key(i);
		head += ": ";
		head += Val(i);
		head += CRLF;
	}
	head += CRLF;
}

LPCSTR ME2MailMessage::GenDate(CLStr& date, time_t *now/*=NULL*/)
{
	time_t nowtm;
	if(!now) {
		nowtm = time(NULL);
		now = &nowtm;
	}
	strftime(date.getbuf(256), 256, "%a, %d %b %Y %H:%M:%S +0800", localtime(now));
	date.endlpstr();
	return date;
}

static int BoundaryNum = 0;
LPCSTR ME2MailMessage::GenBoundary(CLStr& boundary)
{
	return boundary.sprintf("----=_NextPart_%03X_%04X_%08X.%s", BoundaryNum++, getpid(), time(NULL), ME2MAIL_VERSION);
}

LPCSTR ME2MailMessage::GenGUID(CLStr& guid)
{
	return guid.sprintf("%ld.%d.%d", time(NULL), getpid(), BoundaryNum++);
}
CLStr RFC822Encode(LPCSTR src, LPCSTR charset) {
	if(!src || !*src) return "";
	int nlen = strlen(src);
	int nsize = nlen*2;
	char* lpdest = (char*)malloc(nsize);
	Base64_encode((const unsigned char*)src, nlen, (unsigned char*)lpdest, &nsize);
	CLStr dst = "=?";
	dst+=charset;
	dst+="?B?";
	dst+=lpdest;
	dst+="?=";
	free(lpdest);
	return dst;
}
void ME2MailMessage::AddMsgHead(int bQmail, LPCSTR from, LPCSTR to, LPCSTR cc, LPCSTR bcc, LPCSTR subject, LPCSTR yxj, LPCSTR reply, LPCSTR charset/*=gb2312*/)
{
	CLStr tmp, guid;
	if(!bQmail) {
		pHeaders->AddTail("Return-Path", from);
		pHeaders->AddTail("Received", tmp.sprintf("from unknown(%s) by %s directly", from, ME2MAIL_VERSION));
		pHeaders->AddTail("Message-ID", tmp.sprintf("<%s@>", GenGUID(guid)));
	}
	pHeaders->AddTail("From", from);
	pHeaders->AddTail("To", to);
	if(cc) pHeaders->AddTail("Cc", cc);
	if(bQmail && bcc) pHeaders->AddTail("Bcc", bcc);
	pHeaders->AddTail("Subject", RFC822Encode(subject, charset));
	pHeaders->AddTail("Date", GenDate(tmp));
	pHeaders->AddTail("X-HMSpam", "-2");
	pHeaders->AddTail("X-Mailer", ME2MAIL_VERSION);
	pHeaders->AddTail("X-Priority", yxj);
	if(reply) pHeaders->AddTail("Disposition-Notification-To", reply);
	pHeaders->AddTail("MIME-Version", "1.0");
	if(!multiParts.IsEmpty()) pHeaders->AddTail("Content-Type", tmp.sprintf("multipart/mixed;\n\tboundary=\"%s\"", GenBoundary(guid)));
	((ME2MailBodyPart*)bodyParts[0])->pHeaders->AddTail("Content-Type", "text/plain; charset=\""+CLStr(charset)+"\"");
	((ME2MailBodyPart*)bodyParts[0])->pHeaders->AddTail("Content-Transfer-Encoding", "binary");
}
void ME2MailMessage::SetMsgHead(LPCSTR from, LPCSTR to, LPCSTR cc, LPCSTR bcc, LPCSTR subject, LPCSTR yxj, LPCSTR reply)
{
	if(!pHeaders) return;
	CLStr tmp;
	pHeaders->SetValI("From", from);
	pHeaders->SetValI("To", to);
	pHeaders->SetValI("Cc", cc);
	pHeaders->SetValI("Bcc", bcc);
	pHeaders->SetValI("Subject", subject);
	pHeaders->SetValI("Date", GenDate(tmp));
	pHeaders->SetValI("X-Mailer", ME2MAIL_VERSION);
	pHeaders->SetValI("X-Priority", yxj);
	pHeaders->SetValI("Disposition-Notification-To", reply);
}
void ME2MailMessage::AddMsgContent(LPCSTR msgbody, LPCSTR sign, LPCSTR charset/*="gb2312"*/)
{
	if(bodyParts.IsEmpty()) {
		ME2MailHeaders* head = new ME2MailHeaders;
		head->AddTail("Content-Type", "text/plain; charset=\""+CLStr(charset)+"\"");
		head->AddTail("Content-Transfer-Encoding", "binary");
		ME2MailBodyPart* p = NULL;
		if(!multiParts.IsEmpty()) p = (ME2MailBodyPart*)multiParts[0];
		bodyParts.AddTail(new ME2MailBodyPart(head, p)); 
	}
	((ME2MailBodyPart*)bodyParts[0])->content = msgbody;
	if(sign && *sign) {
		((ME2MailBodyPart*)bodyParts[0])->content += sign;
	}
}

void ME2MailMessage::AddAttachement(LPCSTR lpszAttFile, LPCSTR name)
{
	if(multiParts.IsEmpty()) return;
	CLStr content;
	content.read_file(lpszAttFile);
	AddBodyPart("application/ms-download", "base64", content, name, (ME2MailBodyPart*)multiParts[0]);
}

void ME2MailBodyPart::WriteBodyPart(CLStr& out)
{
	CLStr tmp, encoding;
	if(pParent) {
		tmp = pParent->pHeaders->GetHeader("Content-Type", "boundary");
		if(!tmp.empty()) out += CRLF"--" + tmp + CRLF;
	}
	pHeaders->WriteHeaders(out);
	if(!content.empty()) {
		int newsz = content.length()*2;
		encoding = pHeaders->GetHeader("Content-Transfer-Encoding");

		if(encoding.findi("quoted") != -1 && encoding.findi("printable") != -1 )
			QuotedPrint_encode((LPCBYTE)(LPCSTR)content, content.length(), (LPBYTE)tmp.getbuf(newsz), &newsz);
		else if(encoding.findi("base64") != -1)
			Base64_encode((LPCBYTE)(LPCSTR)content, content.length(), (LPBYTE)tmp.getbuf(newsz), &newsz, 1);
		else if( encoding.findi("uuencode") != -1 || encoding.findi("x-uue") != -1 || encoding.findi("x-uuencode") != -1)
			UU_encode((LPCBYTE)(LPCSTR)content, content.length(), (LPBYTE)tmp.getbuf(newsz), &newsz);
		else {
			newsz = content.length();
			tmp = content;
		}
		tmp.setmaxlength(newsz);
		out += tmp + CRLF; 
	}
}

void ME2MailMessage::WriteMessage(CLStr &msg, ME2MailBodyPart* p) const
{
	int i;
	for(i = 0; i < multiParts.GetCount(); i++) {
		if(((ME2MailBodyPart*)multiParts[i])->pParent == p) {
			((ME2MailBodyPart*)multiParts[i])->WriteBodyPart(msg);
			WriteMessage(msg, (ME2MailBodyPart*)multiParts[i]);
		}
	}
	for(i = 0; i < bodyParts.GetCount(); i++) {
		if(((ME2MailBodyPart*)bodyParts[i])->pParent == p) 
			((ME2MailBodyPart*)bodyParts[i])->WriteBodyPart(msg);
	}
	if(p) {
		CLStr boundary = p->pHeaders->GetHeader("Content-Type", "boundary");
		if(!boundary.empty()) msg += CRLF"--" + boundary + "--"CRLF;
	}	
}

void ME2MailMessage::AddBodyPart(LPCSTR ctype, LPCSTR encoding, const CLStr& content, LPCSTR name, ME2MailBodyPart* pParent)
{
	CLStr tmp;
	ME2MailHeaders* head = new ME2MailHeaders;
	head->AddTail("Content-Type", tmp.sprintf("%s; name=\"%s\"", ctype, name));
	head->AddTail("Content-Transfer-Encoding", encoding);
	head->AddTail("Content-Disposition", tmp.sprintf("attachment; filename=\"%s\"", name));
	ME2MailBodyPart* p = new ME2MailBodyPart(head, pParent);
	p->content = content;
	bodyParts.AddTail(p); 
}

void ME2MailMessage::AddForwardMsg(const CLStr& content)
{
	if(multiParts.IsEmpty()) return;
	AddBodyPart("message/rfc822", "7bit", content, "ForwardMessage.eml", (ME2MailBodyPart*)multiParts[0]);
}

void ME2MailMessage::RemoveBodyPart(int pos)
{
	ME2MailBodyPart* p = (ME2MailBodyPart*)bodyParts[pos];
	if(p->pHeaders != pHeaders) {
		bodyParts.RemoveAt(pos);
		delete p;
	}
}

void ME2MailMessage::Open(LPCSTR msg, int bDecodeAll/* = 1*/)
{
	Close();
	CLStr msgstr;
	msgstr.read_file(msg);
	ReadBodyPart(msgstr, NULL, bDecodeAll);
}
void ME2MailMessage::Open2(LPCSTR msg, int bDecodeAll/* = 1*/)
{
	Close();
	CLStr msgstr;
	msgstr.read_file(msg);
	ME2Str str(msgstr);
	ReadBodyPart(str, NULL, bDecodeAll);
}
void ME2MailMessage::Close()
{
	int i;
	for(i = 0; i < bodyParts.GetCount(); i++) 
		delete (ME2MailBodyPart*)bodyParts[i];
	for(i = 0; i < multiParts.GetCount(); i++) 
		delete (ME2MailBodyPart*)multiParts[i];
	bodyParts.RemoveAll();
	multiParts.RemoveAll();
}

int ME2MailHeaders::ReadHeadersFromFile(LPCSTR msgfile)
{
/* v1.0
	CLStr msghead, block;
	CLFile msgf(msgfile, "r");
	do {
		msgf.read(block, 4096);
		msghead += block;
	} while(msghead.find("\n\n") == -1 && msghead.find("\r\n\r\n") == -1&&!block.empty());
	return ReadHeaders(msghead);

*/
// v2.0
	CLStr msghead;
	char block[1024];
	FILE* fp = fopen(msgfile, "r");
	if(fp) {
		while(fgets(block, 1024, fp)) {
			msghead += block;
			if(!block[0]||block[0]=='\r'||block[0]=='\n'||msghead.length()>102400) break;
		}
		fclose(fp);
	}	
	return ReadHeaders(msghead);
}


ME2MailBodyPart* ME2MailMessage::GetContentBodyPart(int* pos/* = NULL*/, int bAlt/*=1*/) const
{
	ME2MailBodyPart *p, *ptmp;
	if(pos) *pos = -1;
	if(bodyParts.IsEmpty()) return NULL;
	p = (ME2MailBodyPart*)bodyParts[0];
	CLStr ctype = p->pHeaders->GetHeader("Content-Type");
	if(ctype.findi("application/") != -1) return NULL;
	if(pos) *pos = 0;
	if(bAlt) {
		if(ctype.findi("text/html") != -1) return p;
		if(bodyParts.GetCount() > 1) {
			ptmp = (ME2MailBodyPart*)bodyParts[1];
			if(ptmp->pHeaders->GetHeader("Content-Type").findi("text/html") != -1
				&& ptmp->pParent 
				&& ptmp->pParent->pHeaders->GetHeader("Content-Type").findi("multipart/alternative") != -1) {
				if(pos) *pos = 1;
				return ptmp;
			}
		}
	}
	if(!p->pParent || ctype.empty() || ctype.findi("text/") != -1) return p;
	if(pos) *pos = -1;
	return NULL;
}

int ME2MailMessage::GetCIDList(CLStrList &cidList) const
{
	CLStr cid;
	for(int i = 0; i < bodyParts.GetCount(); i++) {
		((ME2MailBodyPart*)bodyParts[i])->GetContentID(cid); 
		if(!cid.empty()) cidList.append(cid);
	}
	return cidList.length();
}

void ME2MailMessage::Create(int bMIME)
{
	Close();
	pHeaders = new ME2MailHeaders;
	ME2MailBodyPart* p = new ME2MailBodyPart(pHeaders);
	if(bMIME) {
		p->content = "This is a multi-part message in MIME format, produced by ";
		p->content += ME2MAIL_VERSION;
		multiParts.AddTail(p);
		bodyParts.AddTail(new ME2MailBodyPart(new ME2MailHeaders, p));
	} else 	{
		bodyParts.AddTail(p);
	}
}

void ME2MailBodyPart::GetContentID(CLStr &cid)
{
	cid = pHeaders->GetHeader("Content-ID");
	cid.trim("<>");
}

void ME2MailBodyPart::GetContentLocation(CLStr &cl)
{
	cl = pHeaders->GetHeader("Content-Location");
}

void ME2MailMessage::SetAsMIME(LPCSTR ctype)
{
	if(multiParts.IsEmpty()) {
		CLStr tmp, guid;
		ME2MailBodyPart* pMultiPart = new ME2MailBodyPart(pHeaders);
		multiParts.AddTail(pMultiPart); 

		ME2MailHeaders* head = new ME2MailHeaders;
		((ME2MailBodyPart*)bodyParts[0])->pHeaders = head;
		((ME2MailBodyPart*)bodyParts[0])->pParent = pMultiPart;
		pHeaders->MoveHeader("Content-Type", head);
		pHeaders->MoveHeader("Content-Transfer-Encoding", head);

		pHeaders->SetValI("Content-Type", tmp.sprintf("%s;\n\tboundary=\"%s\"", ctype, GenBoundary(guid)));
		pMultiPart->content = "This is a multi-part message in MIME format, produced by ";
		pMultiPart->content += ME2MAIL_VERSION;
	}
}

void ME2MailMessage::GetContent(CLStr &content, int bAlt/*=1*/)
{
	ME2MailBodyPart* p = GetContentBodyPart(NULL, bAlt);
	if(p) content = p->content;
	else content = "";
}

int ME2MailMessage::IsAttachment(int pos)
{
	if(((ME2MailBodyPart*)bodyParts[pos])->pHeaders->GetHeader("Content-ID").empty()) return 1;
	if(((ME2MailBodyPart*)bodyParts[pos])->pHeaders->GetHeader("Content-Disposition").findi("attachment")!=-1) return 1;
	return 0;
}

int ME2MailMessage::GetAttachmentList(CLStrList &attList, int pos)
{
	CLStr posStr;
	if(pos < 0) {
		GetContentBodyPart(&pos);
		pos++;
	}
	for(int i = pos; i < bodyParts.GetCount(); i++) {
		if(IsAttachment(i)) attList.append(posStr.sprintf("%d", i));
	}
	return attList.length();
}

int ME2MailHeaders::GetPriority() const
{
	int yxj = atoi(GetHeader("X-Priority"));
	if(yxj <= 0) yxj = 3;
	return yxj;
}

time_t ME2MailHeaders::GetDate(struct tm* tm) const
{
	return RFC822Date2Tm(GetHeader("Date"), tm);
}
LPCSTR RFC822DateWeek[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
LPCSTR RFC822DateMonth[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};

#ifdef _WIN32
#define timegm mktime
#endif

time_t ME2MailHeaders::RFC822Date2Tm(const CLStr& dt, struct tm *tm)
{
	CLStrList dtlist, tmlist;
	CLStr date;
	int nIndex;
	struct tm tm2;

	memset(&tm2, 0, sizeof(tm2));
	if(dt.find(",") == -1) date = ", " + dt;
	else date = dt;

	date.split(" ", dtlist, 0, 6);

	// 1. Week
	for(nIndex = 0; nIndex < 7; nIndex++) {
		if(dtlist[0].findi(RFC822DateWeek[nIndex]) != -1)
			break;
	}
	tm2.tm_wday = nIndex;

	// 2. Day
	tm2.tm_mday = atoi(dtlist[1]);

	// 3. Month
	for(nIndex = 0; nIndex < 12; nIndex++) {
		if(dtlist[2].findi(RFC822DateMonth[nIndex]) != -1)
			break;
	}
	if(nIndex == 12) {
		if(tm) *tm = tm2;
		return 0;
	}
	tm2.tm_mon = nIndex;

	// 4. Year
	tm2.tm_year = atoi(dtlist[3]) - 1900;

	// 5. Time
	dtlist[4].split(":", tmlist, 1, 3);
	tm2.tm_hour = atoi(tmlist[0]);
	tm2.tm_min = atoi(tmlist[1]);
	tm2.tm_sec = atoi(tmlist[2]);
	
	// 6. Diff
	int sec_diff = ( - atoi(dtlist[5])) * 36;

	time_t ttt = timegm(&tm2) + sec_diff;
	if(tm) {
		struct tm* ltm = localtime(&ttt);
		*tm = *ltm;
	}

	return ttt;
}
